/*
 * 版权所有 (c) 2016 Zibin Zheng <znbin@qq.com>
 * 保留所有权利
 */

#include "multi_button.h"
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_gpio.h"

// 回调执行宏，带空指针检查
#define EVENT_CB(ev)   do { if(handle->cb[ev]) handle->cb[ev](handle); } while(0)

// 按键句柄链表头指针
static Button* head_handle = NULL;

// 前置声明
static void button_handler(Button* handle);
static inline uint8_t button_read_level(Button* handle);

/**
  * @brief  初始化按键句柄结构体
  * @param  handle: 按键句柄结构体指针
  * @param  button_port: 按键端口
  * @param  button_pin: 按键引脚
  * @param  active_level: 按下时的有效电平
  * @retval 无	
  */
void button_init(Button* handle, GPIO_TypeDef* button_port, uint16_t button_pin, uint8_t active_level)
{
    if (!handle) return;  // 参数校验

    memset(handle, 0, sizeof(Button));
    handle->event = (uint8_t)BTN_NONE_PRESS;
    handle->button_level = !active_level;  // 初始化为与有效电平相反
    handle->active_level = active_level;
    handle->button_port = button_port;
    handle->button_pin = button_pin;
    handle->state = BTN_STATE_IDLE;
}

/**
  * @brief  绑定按键事件回调函数
  * @param  handle: 按键句柄结构体指针
  * @param  event: 触发事件类型
  * @param  cb: 回调函数
  * @retval 无
  */
void button_attach(Button* handle, ButtonEvent event, BtnCallback cb)
{
	if (!handle || event >= BTN_EVENT_COUNT) return;  // 参数校验
	handle->cb[event] = cb;
}

/**
  * @brief  解绑按键事件回调函数
  * @param  handle: 按键句柄结构体指针
  * @param  event: 触发事件类型
  * @retval 无
  */
void button_detach(Button* handle, ButtonEvent event)
{
	if (!handle || event >= BTN_EVENT_COUNT) return;  // 参数校验
	handle->cb[event] = NULL;
}

/**
  * @brief  获取当前发生的按键事件
  * @param  handle: 按键句柄结构体指针
  * @retval 按键事件
  */
ButtonEvent button_get_event(Button* handle)
{
	if (!handle) return BTN_NONE_PRESS;
	return (ButtonEvent)(handle->event);
}

/**
  * @brief  获取按键重复按下的次数
  * @param  handle: 按键句柄结构体指针
  * @retval 重复次数
  */
uint8_t button_get_repeat_count(Button* handle)
{
	if (!handle) return 0;
	return handle->repeat;
}

/**
  * @brief  重置按键状态为空闲
  * @param  handle: 按键句柄结构体指针
  * @retval 无
  */
void button_reset(Button* handle)
{
	if (!handle) return;
	handle->state = BTN_STATE_IDLE;
	handle->ticks = 0;
	handle->repeat = 0;
	handle->event = (uint8_t)BTN_NONE_PRESS;
	handle->debounce_cnt = 0;
}

/**
  * @brief  检查按键当前是否被按下
  * @param  handle: 按键句柄结构体指针
  * @retval 1: 按下, 0: 未按下, -1: 错误
  */
int button_is_pressed(Button* handle)
{
	if (!handle) return -1;
	return (handle->button_level == handle->active_level) ? 1 : 0;
}

/**
  * @brief  内联优化读取按键电平
  * @param  handle: 按键句柄结构体指针
  * @retval 按键电平
  */
static inline uint8_t button_read_level(Button* handle)
{
	return HAL_GPIO_ReadPin(handle->button_port, handle->button_pin);
}

/**
  * @brief  按键驱动核心函数，状态机
  * @param  handle: 按键句柄结构体指针
  * @retval 无
  */
static void button_handler(Button* handle)
{
	uint8_t read_gpio_level = button_read_level(handle);

	// 非空闲状态时计时
	if (handle->state > BTN_STATE_IDLE) {
		handle->ticks++;
	}

	/*------------按键消抖处理---------------*/
	if (read_gpio_level != handle->button_level) {
		// 持续读取到新电平，计数消抖
		if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {
			handle->button_level = read_gpio_level;
			handle->debounce_cnt = 0;
		}
	} else {
		// 电平未变化，消抖计数清零
		handle->debounce_cnt = 0;
	}

	/*-----------------状态机-------------------*/
	switch (handle->state) {
	case BTN_STATE_IDLE:
		if (handle->button_level == handle->active_level) {
			// 检测到按下
			handle->event = (uint8_t)BTN_PRESS_DOWN;
			EVENT_CB(BTN_PRESS_DOWN);
			handle->ticks = 0;
			handle->repeat = 1;
			handle->state = BTN_STATE_PRESS;
		} else {
			handle->event = (uint8_t)BTN_NONE_PRESS;
		}
		break;

	case BTN_STATE_PRESS:
		if (handle->button_level != handle->active_level) {
			// 松开按键
			handle->event = (uint8_t)BTN_PRESS_UP;
			EVENT_CB(BTN_PRESS_UP);
			handle->ticks = 0;
			handle->state = BTN_STATE_RELEASE;
		} else if (handle->ticks > LONG_TICKS) {
			// 检测到长按
			handle->event = (uint8_t)BTN_LONG_PRESS_START;
			EVENT_CB(BTN_LONG_PRESS_START);
			handle->state = BTN_STATE_LONG_HOLD;
		}
		break;

	case BTN_STATE_RELEASE:
		if (handle->button_level == handle->active_level) {
			// 再次按下
			handle->event = (uint8_t)BTN_PRESS_DOWN;
			EVENT_CB(BTN_PRESS_DOWN);
			if (handle->repeat < PRESS_REPEAT_MAX_NUM) {
				handle->repeat++;
			}
			EVENT_CB(BTN_PRESS_REPEAT);
			handle->ticks = 0;
			handle->state = BTN_STATE_REPEAT;
		} else if (handle->ticks > SHORT_TICKS) {
			// 超时，判断单击/双击
			if (handle->repeat == 1) {
				handle->event = (uint8_t)BTN_SINGLE_CLICK;
				EVENT_CB(BTN_SINGLE_CLICK);
			} else if (handle->repeat == 2) {
				handle->event = (uint8_t)BTN_DOUBLE_CLICK;
				EVENT_CB(BTN_DOUBLE_CLICK);
			}
			handle->state = BTN_STATE_IDLE;
		}
		break;

	case BTN_STATE_REPEAT:
		if (handle->button_level != handle->active_level) {
			// 松开按键
			handle->event = (uint8_t)BTN_PRESS_UP;
			EVENT_CB(BTN_PRESS_UP);
			if (handle->ticks < SHORT_TICKS) {
				handle->ticks = 0;
				handle->state = BTN_STATE_RELEASE;  // 继续等待更多按下
			} else {
				handle->state = BTN_STATE_IDLE;  // 序列结束
			}
		} else if (handle->ticks > SHORT_TICKS) {
			// 按下时间过长，视为普通按下
			handle->state = BTN_STATE_PRESS;
		}
		break;

	case BTN_STATE_LONG_HOLD:
		if (handle->button_level == handle->active_level) {
			// 持续长按
			handle->event = (uint8_t)BTN_LONG_PRESS_HOLD;
			EVENT_CB(BTN_LONG_PRESS_HOLD);
		} else {
			// 长按后松开
			handle->event = (uint8_t)BTN_PRESS_UP;
			EVENT_CB(BTN_PRESS_UP);
			handle->state = BTN_STATE_IDLE;
		}
		break;

	default:
		// 非法状态，重置为空闲
		handle->state = BTN_STATE_IDLE;
		break;
	}
}

/**
  * @brief  启动按键，加入工作链表
  * @param  handle: 目标按键句柄
  * @retval 0: 成功, -1: 已存在, -2: 参数无效
  */
int button_start(Button* handle)
{
	if (!handle) return -2;  // 参数无效
	
	Button* target = head_handle;
	while (target) {
		if (target == handle) return -1;  // 已存在
		target = target->next;
	}
	
	handle->next = head_handle;
	head_handle = handle;
	return 0;
}

/**
  * @brief  停止按键，移出工作链表
  * @param  handle: 目标按键句柄
  * @retval 无
  */
void button_stop(Button* handle)
{
	if (!handle) return;  // 参数校验
	
	Button** curr;
	for (curr = &head_handle; *curr; ) {
		Button* entry = *curr;
		if (entry == handle) {
			*curr = entry->next;
			entry->next = NULL;  // 清除next指针
			return;
		} else {
			curr = &entry->next;
		}
	}
}

/**
  * @brief  按键后台定时处理函数，定时周期5ms
  * @param  无
  * @retval 无
  */
void button_ticks(void)
{
	Button* target;
	for (target = head_handle; target; target = target->next) {
		button_handler(target);
	}
}